/*-------------------------------------------------------------------------
 *
 * spgxlog.h
 *	  SP-GiST 访问方法的 xlog 声明。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/spgxlog.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SPGXLOG_H
#define SPGXLOG_H

#include "access/xlogreader.h"
#include "lib/stringinfo.h"
#include "storage/off.h"

/* SPGiST 的 XLOG 记录类型 */
 /* #define XLOG_SPGIST_CREATE_INDEX       0x00 */	/* 不再使用 */
#define XLOG_SPGIST_ADD_LEAF		0x10
#define XLOG_SPGIST_MOVE_LEAFS		0x20
#define XLOG_SPGIST_ADD_NODE		0x30
#define XLOG_SPGIST_SPLIT_TUPLE		0x40
#define XLOG_SPGIST_PICKSPLIT		0x50
#define XLOG_SPGIST_VACUUM_LEAF		0x60
#define XLOG_SPGIST_VACUUM_ROOT		0x70
#define XLOG_SPGIST_VACUUM_REDIRECT 0x80

/*
 * 一些重做函数需要一个 SpGistState，尽管只有少数字段
 * 需要有效。spgxlogState 在 xlog 记录中携带所需的信息。
 * （有关更多说明，请参见 spgxlog.c 中的 fillFakeState。）
 */
typedef struct spgxlogState
{
	TransactionId myXid;
	bool		isBuild;
} spgxlogState;

/*
 * 备份块 0：叶元组的目标页面
 * 备份块 1：父页面（如果有）
 */
typedef struct spgxlogAddLeaf
{
	bool		newPage;		/* 初始化目标页面？ */
	bool		storesNulls;	/* 页面在 nulls 树中？ */
	OffsetNumber offnumLeaf;	/* 叶元组放置的位置偏移量 */
	OffsetNumber offnumHeadLeaf;	/* 链中头元组的偏移量（如果有的话） */

	OffsetNumber offnumParent;	/* 父级下行链接的位置（如果有的话） */
	uint16		nodeI;

	/* new leaf tuple follows (unaligned!) */
} spgxlogAddLeaf;

/*
 * 备份块 0：源叶子页面
 * 备份块 1：目标叶子页面
 * 备份块 2：父页面
 */
typedef struct spgxlogMoveLeafs
{
	uint16		nMoves;			/* 从源页面移动的元组数量 */
	bool		newPage;		/* 初始化目标页面？ */
	bool		replaceDead;	/* 我们是否替换一个已删除的源元组？ */
	bool		storesNulls;	/* 页面是否在空值树中？ */

	/* 父下行链接的位置 */
	OffsetNumber offnumParent;
	uint16		nodeI;

	spgxlogState stateSrc;

	/*----------
	 * 数据如下：
	 *		已删除元组编号的数组，长度为 nMoves
	 *		插入元组编号的数组，长度为 nMoves + 1 或 1
	 *		叶子元组的列表，长度为 nMoves + 1 或 1（未对齐！）
	 *
	 * 注意：如果 replaceDead 为 true，则数据中只有一个插入元组编号
	 * 和一个叶子元组，因为我们不从源中复制已删除的元组
	 *----------
	 */
	OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogMoveLeafs;

#define SizeOfSpgxlogMoveLeafs	offsetof(spgxlogMoveLeafs, offsets)

/*
 * 备份块 0：原始页面
 * 备份块 1：新元组去往的位置（如果不是同一位置）
 * 备份块 2：父下行链接的位置（如果已更新且与旧的和新的不同）
 */
typedef struct spgxlogAddNode
{
	/*
	 * 原始页面中原始内元组的偏移量（在备份块 0 上）。
	 */
	OffsetNumber offnum;

	/*
	 * 新页面中新元组的偏移量（在备份块 1 上）。无效，
	 * 如果我们在原始页面中覆盖了旧元组）。
	 */
	OffsetNumber offnumNew;
	bool		newPage;		/* 初始化新页面？ */

	/*----
	 * 父下行链路在哪里？ parentBlk 指示它在的页面，
	 * offnumParent 是页面内的偏移量。parentBlk 的可能值为：
	 *
	 * 0: parent == 原始页面
	 * 1: parent == 新页面
	 * 2: parent == 不同页面（blk 参照 2）
	 * -1: parent 未更新
	 *----
	 */
	int8		parentBlk;
	OffsetNumber offnumParent;	/* 在父页面中的偏移量 */

	uint16		nodeI;

	spgxlogState stateSrc;

	/*
	 * 更新的内部元组跟随（未对齐！）
	 */
} spgxlogAddNode;

/*
 * 备份块 0：前缀元组的位置
 * 备份块 1：后缀元组的位置（如果不同页面）
 */
typedef struct spgxlogSplitTuple
{
	/* 前缀元组的位置 */
	OffsetNumber offnumPrefix;

	/* 后缀元组的位置 */
	OffsetNumber offnumPostfix;
	bool		newPage;		/* 需要初始化该页面吗？ */
	bool		postfixBlkSame; /* 后缀元组是否放在与前缀相同的页面上？ */

	/*
	 * 新的前缀内部元组跟随，然后是新的后缀内部元组（两者都
	 * 是未对齐的！）
	 */
} spgxlogSplitTuple;

/*
 * rdata 数组中的缓冲区引用是：
 * 备份块 0：源页面（仅在不为根时）
 * 备份块 1：目标页面（如果使用）
 * 备份块 2：内部页面
 * 备份块 3：父页面（如果有，并且与内部不同）
 */
typedef struct spgxlogPickSplit
{
	bool		isRootSplit;

	uint16		nDelete;		/* 要从源删除的 n */
	uint16		nInsert;		/* 要在源和/或目标上插入的 n */
	bool		initSrc;		/* 重新初始化源页面？ */
	bool		initDest;		/* 重新初始化目标页面？ */

	/* 新的内部元组放置的位置 */
	OffsetNumber offnumInner;
	bool		initInner;		/* 重新初始化内部页面？ */

	bool		storesNulls;	/* 页面是否在空值树中？ */

	/* 父级下行链接的位置（如果有的话） */
	bool		innerIsParent;	/* 是否父页与内部页相同？ */
	OffsetNumber offnumParent;
	uint16		nodeI;

	spgxlogState stateSrc;

	/*----------
	 * 数据如下：
	 *		已删除的元组编号数组，长度 nDelete
	 *		已插入的元组编号数组，长度 nInsert
	 *		已插入元组的页面选择器字节数组，长度 nInsert
	 *		新的内部元组（未对齐！）
	 *		叶子元组的列表，长度 nInsert（未对齐！）
	 *----------
	 */
	OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogPickSplit;

#define SizeOfSpgxlogPickSplit offsetof(spgxlogPickSplit, offsets)

typedef struct spgxlogVacuumLeaf
{
	uint16		nDead;			/* 将变为 DEAD 的元组数量 */
	uint16		nPlaceholder;	/* 将变为 PLACEHOLDER 的元组数量 */
	uint16		nMove;			/* 要移动的元组数量 */
	uint16		nChain;			/* 要重新链接的元组数量 */

	spgxlogState stateSrc;

	/*----------
	 * 数据如下：
	 *		将变为 DEAD 的元组编号
	 *		将变为 PLACEHOLDER 的元组编号
	 *		要移动的元组编号（并用 PLACEHOLDER 替换）
	 *		要移动到的元组编号（替换原有内容）
	 *		要更新 nextOffset 链接的元组编号
	 *		要插入到 nextOffset 链接中的元组编号
	 *----------
	 */
	OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogVacuumLeaf;

#define SizeOfSpgxlogVacuumLeaf offsetof(spgxlogVacuumLeaf, offsets)

typedef struct spgxlogVacuumRoot
{
	/* 当根页也是叶子时进行清理 */
	uint16		nDelete;		/* 要删除的元组数量 */

	spgxlogState stateSrc;

	/* 要删除的元组的偏移量 */
	OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogVacuumRoot;

#define SizeOfSpgxlogVacuumRoot offsetof(spgxlogVacuumRoot, offsets)

typedef struct spgxlogVacuumRedirect
{
	uint16		nToPlaceholder; /* 要创建占位符的重定向数量 */
	OffsetNumber firstPlaceholder;	/* 要移除的第一个占位符元组 */
	TransactionId newestRedirectXid;	/* 被移除的重定向的最新 XID */

	/* 要创建占位符的重定向元组的偏移量 */
	OffsetNumber offsets[FLEXIBLE_ARRAY_MEMBER];
} spgxlogVacuumRedirect;

#define SizeOfSpgxlogVacuumRedirect offsetof(spgxlogVacuumRedirect, offsets)

extern void spg_redo(XLogReaderState *record);
extern void spg_desc(StringInfo buf, XLogReaderState *record);
extern const char *spg_identify(uint8 info);
extern void spg_xlog_startup(void);
extern void spg_xlog_cleanup(void);
extern void spg_mask(char *pagedata, BlockNumber blkno);

#endif							/* SPGXLOG_H */
